﻿/******************************************************************************
 * Copyright (C) 671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "WorldSession.h"
#include <stdexcept>
#include <NodeCpp/AutoPtr.h>
#include <NodeCpp/String.h>
#include "Shared/Log/Log.h"
#include "Game/SharedDefines.h"
#include "WorldSocketDelegate.h"
#include "World/World.h"
using NodeCpp::AutoPtr;

WorldSession::WorldSession(std::uint32_t _ID, WorldSocketDelegate* _Conn, 
    AccountTypes _Sec, time_t _MuteTime, LocaleConstant _Locale)
    : PeerHost_()
    , AccountId_(_ID), Sock_(_Conn), Security_(_Sec)
    , MuteTime_(_MuteTime), Locale_(_Locale)
    , Packets_()
    , Running_(true)
    , Latency_(0), ClientDelay_(0)

    , BillingTimeRemaining_(0), BillingPlanFlags_(0), BillingTimeRested_(0)
    , Player_(NULL), PlayerLoading_(false)
{
    PeerHost_ = Sock_->GetPeerHost();
}

WorldSession::~WorldSession(void)
{
    _Uninitialize();
}

bool WorldSession::Update(PacketFilter& _Filter)
{
    if (!Running_) {
        /* 需要先关闭连接 */
        return !Sock_->TryClose();
    }

    do
    {
        /********************* 世界正在关闭或网络出错 *********************/
        if (!sWorld.IsRunning()
            || Sock_->IsClosing()) {
                Running_ = false;
                break;
        }

        /********************* 处理数据包 *********************/
        try {
            WorldPacket *_Packet = NULL;
            while (Packets_.Next(_Packet, _Filter))
            {
                AutoPtr<WorldPacket> _ProcessingPacket(_Packet);
                const OpcodeHandler &_Ophandler = sOpCodes[_Packet->opcode()];
                switch (_Ophandler.Status)
                {
                case STATUS_AUTHED:
                    _ExecuteOpcode(_Packet, _Ophandler);
                    break;
                }
            }

            return true;
        }
        catch (std::exception& _E)
        {
            Running_ = false;
            sLog.outError("%s", _E.what());
        }
    } while (false);

    return true;
}

void WorldSession::SendPacket(WorldPacket& _Packet)
{
    Sock_->SendPacket(_Packet);
}

void WorldSession::SendAuthOk(void)
{
    /* 发送响应 */
    WorldPacket _Packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4);
    _Packet << std::uint8_t(AUTH_OK);
    _Packet << std::uint32_t(BillingTimeRemaining_);
    _Packet << std::uint8_t(BillingPlanFlags_);
    _Packet << std::uint32_t(BillingTimeRested_);
    SendPacket(_Packet);
}

void WorldSession::SendAuthWaitQue(std::uint32_t _Position)
{
    if (_Position == 0)
    {
        WorldPacket _Packet(SMSG_AUTH_RESPONSE, 1);
        _Packet << std::uint8_t(AUTH_OK);
        SendPacket(_Packet);
    }
    else
    {
        WorldPacket _Packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 4);
        _Packet << std::uint8_t(AUTH_WAIT_QUEUE);
        _Packet << std::uint32_t(BillingTimeRemaining_);
        _Packet << std::uint8_t(BillingPlanFlags_);
        _Packet << std::uint32_t(BillingTimeRested_);
        // 玩家在队列中的位置
        _Packet << std::uint32_t(_Position);
        SendPacket(_Packet);
    }
}

void WorldSession::_ExecuteOpcode(WorldPacket *_Packet, const OpcodeHandler &_Ophandler)
{
    if (!_Ophandler.Handler) {
        char _ErrMsg[64];
        NodeCpp::snprintf(_ErrMsg, sizeof(_ErrMsg), 
            "Empty opcode handler for OPCODE(%d)", _Packet->opcode());
        throw std::logic_error(_ErrMsg);
    }

    (this->*_Ophandler.Handler)(*_Packet);
}

void WorldSession::HandleServerSide(WorldPacket &_Packet)
{
    sLog.outError("received server-side opcode %s (0x%.4X)",
        LookupOpcodeName(_Packet.opcode()), _Packet.opcode());
}

void WorldSession::_Uninitialize(void)
{
    FREE_QUEUED_PACKETS(Packets_, WorldPacket);
}